#include "form_setting_upgrade.h"
#include "ui_form_setting_upgrade.h"

// add neccesary includes here
#include "ui/share/form_shield_dlg.h"
#include "ui/share/form_prompt_dlg.h"
#include "ui/share/form_inquiry_dlg.h"
#include "utils/upgrade/upgrade_bll.h"

#define DF_UPGRADE_TIMEOUT      300000

/*
 * Function:    FormSettingUpgrade
 * Description: The default constructor
 * Paramer:     void
 * Return:
 */
FormSettingUpgrade::FormSettingUpgrade(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::FormSettingUpgrade)
{
    qDebug() << Q_FUNC_INFO;

    ui->setupUi(this);
}

/*
 * FormHistoryResult:    ~FormSettingUpgrade
 * Description: The default destructor
 * Paramer:     void
 * Return:
 */
FormSettingUpgrade::~FormSettingUpgrade()
{
    qDebug() << Q_FUNC_INFO;

    if(m_pUpgradeGuard) m_pUpgradeGuard->stop();

    if(m_pWorker) {
        m_pWorker->wait(1000);
        m_pWorker->quit();
        m_pWorker->deleteLater();
    }

    delete ui;
}

/*
 * Function:    getInstance
 * Description: Get an instance of "FormSettingUpgrade" class, if exist, return it
 * Paramer:     QWidget *pParent - the pointer of QWidget class
 * Return:      the pointer of "FormSettingUpgrade" class
 */
FormSettingUpgrade* FormSettingUpgrade::getInstance(QWidget *pParent)
{
    static FormSettingUpgrade *pNew = NULL;
    if(!pNew) {
        qDebug() << Q_FUNC_INFO;

        // Create a new object if it doesn't exist
        pNew = new FormSettingUpgrade(pParent);
        if(NULL == pNew) return NULL;

        // After constructing object, initialize the object
        pNew->_initialze();

        // Initialize control's UI
        pNew->_initUi();

        // Hide widget firstly
        pNew->hide();
    }
    else {
        // Set the parent object
        pNew->setParent(pParent);
    }

    return pNew;
}

/*
 * Function:    _initialze
 * Description: After constructing object, initialize the object
 * Paramer:     void
 * Return:      void
 */
void FormSettingUpgrade::_initialze()
{
    m_pWorker = new QThread();
    Q_ASSERT(NULL != m_pWorker);

    Q_ASSERT(NULL != UpgradeBLL::getInstance(this));
    UpgradeBLL::getInstance(this)->moveToThread(m_pWorker);

    m_pUpgradeGuard = new QTimer();
    Q_ASSERT(NULL != m_pUpgradeGuard);

    // Signal-slot initialization
    std::vector<bool> vecConnect;

    vecConnect.push_back(QObject::connect(m_pUpgradeGuard, SIGNAL(timeout()), this, SLOT(slotUpgradeGuard())));
    vecConnect.push_back(QObject::connect(m_pWorker, &QThread::finished, UpgradeBLL::getInstance(this), &QObject::deleteLater));
    vecConnect.push_back(QObject::connect(this,  SIGNAL(signalStartupUpgrade(QString, bool, bool, unsigned int)), UpgradeBLL::getInstance(this), SLOT(slotStartupUpgrade(QString, bool, bool, unsigned int)), static_cast<Qt::ConnectionType>(Qt::AutoConnection | Qt::UniqueConnection)));
    vecConnect.push_back(QObject::connect(UpgradeBLL::getInstance(this), SIGNAL(signalProgressInit(int, int, bool)), this, SLOT(slotProgressInit(int, int, bool)), static_cast<Qt::ConnectionType>(Qt::AutoConnection | Qt::UniqueConnection)));
    vecConnect.push_back(QObject::connect(UpgradeBLL::getInstance(this), SIGNAL(signalProgressValue(int, bool)), this, SLOT(slotProgressValue(int, bool)), static_cast<Qt::ConnectionType>(Qt::AutoConnection | Qt::UniqueConnection)));
    vecConnect.push_back(QObject::connect(UpgradeBLL::getInstance(this), SIGNAL(signalUpgradeResult(int, bool)), this, SLOT(slotUpgradeResult(int, bool)), static_cast<Qt::ConnectionType>(Qt::AutoConnection | Qt::UniqueConnection)));
    vecConnect.push_back(QObject::connect(CanBLL_T1C::getInstance(this), SIGNAL(signalUpgradeIapPayload(QByteArray)), UpgradeBLL::getInstance(this), SLOT(slotUpgradeIapPayload(QByteArray)), static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection)));
    vecConnect.push_back(QObject::connect(CanBLL_T1C::getInstance(this), SIGNAL(signalUpgradeIapResult(QByteArray)), UpgradeBLL::getInstance(this), SLOT(slotUpgradeIapResult(QByteArray)), static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection)));

    for(int i=0; i<(int)vecConnect.size(); i++) {
        Q_ASSERT(vecConnect.at(i));
    }

    m_pWorker->start();
}

/*
* Function:		_initUi
* Description:	Init the window style
* Paramer:		void
* Return:		void
*/
void FormSettingUpgrade::_initUi()
{
    qDebug() << Q_FUNC_INFO;

    // Displays the background image of the window
    QWidget::setAttribute(Qt::WA_StyledBackground);

    SET_STYLE_SHEET(QString(":/%1/setting/%2.qss").arg("skin_1").arg(this->objectName()));

    ui->progressBar_IAP->setValue(0);
    ui->progressBar_IAP->setTextVisible(false);
    ui->progressBar_APP->setValue(0);
    ui->progressBar_APP->setTextVisible(false);

    ui->label_Result_IAP->setText("");
    ui->label_Result_APP->setText("");

    ui->label_IAP->setText("");
    ui->label_APP->setText("");
    ui->label_IAP->setAlignment(Qt::AlignHCenter);
    ui->label_APP->setAlignment(Qt::AlignHCenter);

    ui->radioButton_Motion->setChecked(true);
    ui->checkBox_APP->setChecked(false);
    ui->checkBox_IAP->setChecked(false);
    ui->pushButton_Startup->setEnabled(false);
}

/*
* Function:		changeEvent
* Description:	Triggers an event before it is switch language
* Paramer:		QShowEvent *event - the pointer of "QEvent"
* Return:		void
*/
void FormSettingUpgrade::changeEvent(QEvent *e)
{
    QWidget::changeEvent(e);

    switch (e->type())
    {
    case QEvent::LanguageChange:
        qDebug() << Q_FUNC_INFO;

        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}

/*
 * Function:    on_pushButton_Startup_clicked
 * Description: Start upgrade
 * Paramer:     void
 * Return:      void
 */
void FormSettingUpgrade::on_pushButton_Startup_clicked()
{
    qDebug() << Q_FUNC_INFO;

    // Prevent the button from clicking repeatedly quickly
    FormShieldDlg::getInstance(this)->show();

    m_pUpgradeGuard->setInterval(DF_UPGRADE_TIMEOUT);
    m_pUpgradeGuard->start();

    ui->progressBar_IAP->setValue(0);
    ui->progressBar_APP->setValue(0);
    ui->label_Result_IAP->setText("");
    ui->label_Result_APP->setText("");
    ui->label_IAP->setText("");
    ui->label_APP->setText("");

    ui->pushButton_Startup->setEnabled(false);

    // Startup upgrade process
    unsigned int unIapCanId = 0;
    bool bIap = ui->checkBox_IAP->isChecked();
    bool bApp = ui->checkBox_APP->isChecked();

    QList<unsigned int> lstCanID = QList<unsigned int>() << CanVars::gs_nIapCanID_CH1 << CanVars::gs_nPipetteCanID_CH1 << CanVars::gs_nHeatIapCanID_CH1;
    QList<QRadioButton*> lstDevIAP = QList<QRadioButton*>() << ui->radioButton_Motion << ui->radioButton_Pipette << ui->radioButton_Heating;

    if(bIap) {
        if(lstCanID.size() == lstDevIAP.size()) {
            for(int i=0; i<lstDevIAP.size(); i++) {
                if(lstDevIAP.at(i)->isChecked()) {
                    unIapCanId = lstCanID.at(i);
                    break;
                }
            }
        }
    }

    emit signalStartupUpgrade("", bIap, bApp, unIapCanId);
}

/*
 * Function:    on_checkBox_IAP_stateChanged
 * Description: Enable/Disable the upgrade button after click the checkbox
 * Paramer:     int arg1 - the check status
 * Return:      void
 */
void FormSettingUpgrade::on_checkBox_IAP_stateChanged(int arg1)
{
    qDebug() << Q_FUNC_INFO << arg1;

    if(Qt::CheckState::Unchecked == ui->checkBox_IAP->checkState()
            && Qt::CheckState::Unchecked == ui->checkBox_APP->checkState()) {
        ui->pushButton_Startup->setDisabled(true);

        ui->label_IAP->setText("");
        ui->label_APP->setText("");
    }
    else {
        ui->pushButton_Startup->setDisabled(false);
    }
}

/*
 * Function:    on_checkBox_APP_stateChanged
 * Description: Enable/Disable the upgrade button after click the checkbox
 * Paramer:     int arg1 - the check status
 * Return:      void
 */
void FormSettingUpgrade::on_checkBox_APP_stateChanged(int arg1)
{
    qDebug() << Q_FUNC_INFO << arg1;

    if(Qt::CheckState::Unchecked == ui->checkBox_IAP->checkState()
            && Qt::CheckState::Unchecked == ui->checkBox_APP->checkState()) {
        ui->pushButton_Startup->setDisabled(true);

        ui->label_IAP->setText("");
        ui->label_APP->setText("");
    }
    else {
        ui->pushButton_Startup->setDisabled(false);
    }
}

/*
 * Function:    slotUpgradeGuard
 * Description: The slot guard upgrade process
 * Paramer:     void
 * Return:      void
 */
void FormSettingUpgrade::slotUpgradeGuard()
{
    qDebug() << Q_FUNC_INFO;

    m_pUpgradeGuard->stop();

    ui->checkBox_IAP->setEnabled(true);
    ui->checkBox_APP->setEnabled(true);
    ui->pushButton_Startup->setVisible(true);

    ui->pushButton_Startup->setEnabled(true);

    FormShieldDlg::getInstance(this)->hide();
}

/*
 * Function:    slotProgressInit
 * Description: Get the max range of the progress
 * Paramer:     int nMin - the max range
 *              int nMax - the min range
 *              bool bApp - is APP?
 * Return:      void
 */
void FormSettingUpgrade::slotProgressInit(int nMin, int nMax, bool bApp)
{
    qDebug() << Q_FUNC_INFO << nMin << nMax << bApp;

    if(bApp) {
        ui->progressBar_APP->setRange(nMin, nMax);
        ui->progressBar_APP->setValue(0);
    }
    else {
        ui->progressBar_IAP->setRange(nMin, nMax);
        ui->progressBar_IAP->setValue(0);
    }
}

/*
 * Function:    slotProgressValue
 * Description: Get the value of the progress
 * Paramer:     int nValue - the progress value
 *              bool bApp - is APP?
 * Return:      void
 */
void FormSettingUpgrade::slotProgressValue(int nValue, bool bApp)
{
    qDebug() << Q_FUNC_INFO << nValue << bApp;

    if(bApp) ui->progressBar_APP->setValue(nValue);
    else ui->progressBar_IAP->setValue(nValue);

    ui->progressBar_IAP->repaint();
}

/*
 * Function:    slotUpgradeResult
 * Description: Get the result of upgrade
 * Paramer:     int nErrCode - the upgrade result
 *              bool bApp - is APP?
 * Return:      void
 */
void FormSettingUpgrade::slotUpgradeResult(int nErrCode, bool bApp)
{
    qDebug() << Q_FUNC_INFO << nErrCode << bApp;

    m_pUpgradeGuard->stop();

    if(bApp) {
        if(0 == nErrCode) {
            ui->checkBox_IAP->setChecked(false);
            ui->checkBox_APP->setChecked(false);
            ui->pushButton_Startup->setEnabled(false);
            ui->label_Result_APP->setText("√");
            ui->label_APP->setText(tr("Upgrade completed"));

            QTimer::singleShot(500, this, [=]{
                FormPromptWidget::getInstance(this->parentWidget())->setPromptInfo(tr("Prompt"), tr("Upgrade completed, restarting system"));
            });
        }
        else {
            ui->checkBox_IAP->setChecked(false);
            ui->checkBox_APP->setChecked(false);
            ui->pushButton_Startup->setEnabled(false);
            ui->label_Result_APP->setText("×");
            ui->label_APP->setText(tr("Upgrade not completed") + QString(" [%1]").arg(nErrCode));

            switch (nErrCode) {
            case -1: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("No U disk was detected")); break; }
            case -2: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("The opt directory was not detected")); break; }
            case -3: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("The local installation directory don't exist")); break; }
            case -4: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("The local application directory don't exist")); break; }
            case -5: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("Installation package naming error")); break; }
            case -6: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("The installation package version is too low")); break; }
            case -7: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("The current software is the latest version")); break; }
            case -8: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("The installation package version is low")); break; }
            case -10: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Error"), tr("Installation package copy failed")); break; }
            case -11: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Error"), tr("Installation package decompression failed")); break; }
            case -12: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Error"), tr("File link failed")); break; }
            default: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Error"), tr("Other error") + QString("[%1]").arg(nErrCode)); break; }
            }
        }

        FormShieldDlg::getInstance(this)->hide();
    }
    else {
        if(0 == nErrCode) {
            ui->checkBox_IAP->setChecked(false);
            ui->checkBox_APP->setChecked(false);
            ui->pushButton_Startup->setEnabled(false);
            ui->label_Result_IAP->setText("√");
            ui->label_IAP->setText(tr("Upgrade completed"));

            if(!ui->checkBox_APP->isChecked()) {
                QTimer::singleShot(500, this, [=]{
                    FormPromptWidget::getInstance(this->parentWidget())->setPromptInfo(tr("Prompt"), tr("Upgrade completed, please restart the instrument"));
                });
            }
        }
        else {
            ui->checkBox_IAP->setChecked(false);
            ui->checkBox_APP->setChecked(false);
            ui->pushButton_Startup->setEnabled(false);
            ui->label_Result_IAP->setText("×");
            ui->label_IAP->setText(tr("Upgrade not completed") + QString(" [%1]").arg(nErrCode));

            switch (nErrCode) {
            case -1: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("No U disk was detected")); break; }
            case -2: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("The opt directory was not detected")); break; }
            case -3: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("The local installation directory don't exist")); break; }
            case -4: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("The local application directory don't exist")); break; }
            case -5: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("Installation package naming error")); break; }
            case -6: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("The installation package version is too low")); break; }
            case -7: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("The current software is the latest version")); break; }
            case -8: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Prompt"), tr("The installation package version is low")); break; }
            case -10: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Error"), tr("Installation package copy failed")); break; }
            case -11: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Error"), tr("Installation package decompression failed")); break; }
            case -12: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Error"), tr("File link failed")); break; }
            case -101: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Error"), tr("Upgrade data transfer failed")); break; }
            case -102: { FormPromptWidget::getInstance(this)->setPromptInfo(tr("Error"), tr("CRC verification failed")); break; }
            default: { FormPromptWidget::getInstance(this->parentWidget())->setPromptInfo(tr("Error"), tr("Other error") + QString("[%1]").arg(nErrCode)); break; }
            }

            FormShieldDlg::getInstance(this)->hide();
        }

        if(!ui->checkBox_APP->isChecked()) {
            m_pUpgradeGuard->stop();
            FormShieldDlg::getInstance(this)->hide();
        }
    }
}
